package com.ctg.behavior.calc.impala.builder.retentions;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.ctg.behavior.calc.common.CtCalcDate;
import com.ctg.behavior.calc.impala.report.retentions.CtRetentionsReport;
import com.ctg.behavior.calc.utils.CtCalcPropertyUtils;
import com.ctg.behavior.common.consts.CtFieldConsts;
import com.ctg.behavior.common.retentions.CtRetentionsCalcData;
import com.ctg.behavior.common.retentions.CtRetentionsCell;
import com.ctg.behavior.common.retentions.CtRetentionsRow;
import com.ctg.behavior.utils.CtDateUtils;
import com.ctg.behavior.utils.CtDoubleUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class CtRetentionsCalcResultBuilder {

	@Autowired
	private CtRetentionsRowKeyBuilder rowkeyBuilder;

	// KEY: calcDate+"@"+eventId+"@"+eventDate+"@"+byValues[]
	private static ThreadLocal<Map<String, LinkedHashMap<String, Object>>> tlIndexResults = new ThreadLocal<>();

	// KEY: byValues数组中的元素，使用@符号分隔连接起来。若byFields为空(无分组),则为：$ALL
	private static ThreadLocal<Map<String, CtRetentionsRow>> tlIndexSumRows = new ThreadLocal<>();

	public void resetResultBuilderTheadLocal() {
		Map<String, CtRetentionsRow> indexedRows = new HashMap<>();
		tlIndexSumRows.set(indexedRows);
		Map<String, LinkedHashMap<String, Object>> first0DayMap = new LinkedHashMap<>();
		tlIndexResults.set(first0DayMap);
	}

	public CtRetentionsCalcData doBuilder(CtRetentionsCalcData calcData, List<LinkedHashMap<String, Object>> calcResult, CtRetentionsReport report) {

		// 1. 通用字段
		calcData.setAppId(report.getAppId());
		calcData.setBookmarkId(report.getBookmarkId());
		calcData.setRequestId(report.getRequestId());
		calcData.setReportUpdateTime(CtDateUtils.dateToString(new Date()));
		calcData.setDataUpdateTime(calcData.getDataUpdateTime());
		// 2. ByFields
		calcData.setByFields(report.getByFields());
		calcData.setHasFirstDay(true);
		// 3. 定义必用的全局变量
		List<String> byFields = CtCalcPropertyUtils.getProperties(report.getByFields());
		// 获得本calcDate的基准数,即：事件一在calcDate的total
		String calcDay = report.getWhichDay();
		String firstEventId = report.getFirstEvent().getEventId();
		String secondEventId = report.getSecondEvent().getEventId();
		// 4. 对查询结进行Index
		Map<String, List<String>> byValuesMap = new HashMap<>();
		{
			StringBuilder sbuf = new StringBuilder();
			for (LinkedHashMap<String, Object> oneRowCalcResult : calcResult) {
				String resultKey = rowkeyBuilder.buildResultKey(byFields, oneRowCalcResult);
				tlIndexResults.get().put(resultKey, oneRowCalcResult);

				sbuf.delete(0, sbuf.length());
				sbuf.append("K");
				List<String> byValues = new ArrayList<>();
				for (String byField : byFields) {
					sbuf.append("@").append(oneRowCalcResult.get(byField));
					byValues.add(String.valueOf(oneRowCalcResult.get(byField)));
				}
				if (byFields.isEmpty()) {
					byValues.add(CtFieldConsts.FIELD_KEY_ALL);
				}

				byValuesMap.put(sbuf.toString(), byValues);
			}
		}
		// 5. 以分组为维度进行结果对象组装;byFields为空时,则表示按总体查看，即：byValues[$ALL]
		Set<Entry<String, List<String>>> entries = byValuesMap.entrySet();
		for (Entry<String, List<String>> entry : entries) {

			List<String> byValues = entry.getValue();

			String baseResultKey = rowkeyBuilder.buildResultKey(calcDay, firstEventId, calcDay, byValues);
			Map<String, Object> oneResult = tlIndexResults.get().get(baseResultKey);
			if(oneResult==null) {
				log.error("baseResultKey={}",baseResultKey);
				continue;
			}
			Long baseTotal = Long.valueOf(oneResult.get(CtFieldConsts.FIELD_KEY_TOTAL).toString());
			CtRetentionsRow dayRow = new CtRetentionsRow();
			// 初始化ROW
			dayRow.setByValue(report.getWhichDay());
			dayRow.getByValues().add(report.getWhichDay());
			dayRow.setTotalPeople(baseTotal);
			// 填充本ROW下的对应CELL
			for (CtCalcDate cellDate : report.getCellDates()) {
				String eventDate = CtDateUtils.dateToString(cellDate.getFromDate(), CtDateUtils.YYYY_MM_DD);
				String theDayCellKey = rowkeyBuilder.buildResultKey(calcDay, secondEventId, eventDate, byValues);
				LinkedHashMap<String, Object> cellResult = tlIndexResults.get().get(theDayCellKey);
				Long cellTotal = 0L;
				if(null != cellResult) {
					cellTotal = Long.valueOf(cellResult.get(CtFieldConsts.FIELD_KEY_TOTAL).toString());
				}
				// 流失
				if(report.getIsWastage()) {
					cellTotal = baseTotal - cellTotal;
				}
				CtRetentionsCell oneCell = new CtRetentionsCell();
				dayRow.getCells().add(oneCell);
				oneCell.setPeople(cellTotal);
				oneCell.setPercent(CtDoubleUtils.percent2Scale(cellTotal, baseTotal));
			}

			CtRetentionsRow sumRow = this.doSumRowBuilder(report, calcData, byValues, dayRow);
			log.info("sumRow={}", JSON.toJSONString(sumRow));

			if (byFields.isEmpty()) {
				// 按【总体】查看
				calcData.getRows().add(dayRow);
			} else {
				// 非【总体】查看
				sumRow.getSubRows().add(dayRow);
			}

		}

		return calcData;
	}

	public CtRetentionsRow doSumRowBuilder(CtRetentionsReport report, CtRetentionsCalcData calcData, List<String> byValues, CtRetentionsRow theDayRow) {
		String sumRowKey = rowkeyBuilder.buildSumRowKey(byValues);
		CtRetentionsRow sumRow = tlIndexSumRows.get().get(sumRowKey);
		if (null == sumRow) {
			sumRow = new CtRetentionsRow();
			// 填充byFields对应的分组值
			sumRow.getByValues().addAll(byValues);
			// 没有指定分组字段，则需要填充ALL
			sumRow.setByValue(byValues.get(0));

			for (int index = 0; index < report.getMaxCellIndex(); index++) {
				CtRetentionsCell oneCell = new CtRetentionsCell();
				sumRow.getCells().add(oneCell);
			}
			tlIndexSumRows.get().put(sumRowKey, sumRow);
			calcData.getRows().add(sumRow);
		}

		// 累加到小计ROW
		sumRow.setTotalPeople(sumRow.getTotalPeople() + theDayRow.getTotalPeople());
		for (int index = 0; index < report.getMaxCellIndex(); index++) {
			CtRetentionsCell tmpSumCell = sumRow.getCells().get(index);
			tmpSumCell.setPeople(tmpSumCell.getPeople() + theDayRow.getCells().get(index).getPeople());
			tmpSumCell.setPercent(CtDoubleUtils.percent2Scale(tmpSumCell.getPeople(), sumRow.getTotalPeople()));
		}

		return sumRow;
	}

}
